"""
人流密度分析服务
基于人员检测结果进行密度计算和热力图生成
"""
import asyncio
import logging
import numpy as np
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional, Tuple
from uuid import uuid4
from scipy import ndimage
from sklearn.cluster import DBSCAN
import cv2

from schemas.person_detection import (
    PersonDetectionResult, PersonInfo, DensityAnalysisResult,
    DensityConfig, HeatmapData, CrowdAlert, AlertLevel
)
from services.campus_crowd_safety.person_detection_service import person_detection_service
from core.config import get_settings

logger = logging.getLogger(__name__)
settings = get_settings()


class CrowdDensityAnalyzer:
    """人群密度分析器"""
    
    def __init__(self, config: DensityConfig):
        self.config = config
        self.grid_width, self.grid_height = config.grid_size
        self.density_history: Dict[str, List[DensityAnalysisResult]] = {}
        self.heatmap_history: Dict[str, List[HeatmapData]] = {}
        self.alert_history: Dict[str, List[CrowdAlert]] = {}
        
        # 平滑滤波器
        self.smoothing_kernel = self._create_smoothing_kernel()
    
    def _create_smoothing_kernel(self) -> np.ndarray:
        """创建平滑滤波器"""
        kernel_size = 3
        kernel = np.ones((kernel_size, kernel_size), dtype=np.float32)
        kernel = kernel / np.sum(kernel)
        return kernel
    
    async def analyze_density(
        self, 
        detection_result: PersonDetectionResult,
        image_width: int,
        image_height: int
    ) -> DensityAnalysisResult:
        """分析人群密度"""
        try:
            analysis_id = str(uuid4())
            
            # 提取人员位置
            person_positions = []
            for person_data in detection_result.persons:
                center = person_data.get("center", [0, 0])
                person_positions.append(center)
            
            # 计算网格密度
            density_grid = self._calculate_grid_density(
                person_positions, image_width, image_height
            )
            
            # 计算总体密度评分
            total_area = image_width * image_height
            person_area_sum = sum(
                person_data.get("features", {}).get("area", 100) 
                for person_data in detection_result.persons
            )
            density_score = min(person_area_sum / total_area, 1.0)
            
            # 确定密度等级
            density_level = self._get_density_level(density_score)
            
            # 分析区域
            regions = self._analyze_regions(density_grid, image_width, image_height)
            
            # 检测热点
            hotspots = self._detect_hotspots(person_positions)
            
            # 创建分析结果
            result = DensityAnalysisResult(
                analysis_id=analysis_id,
                camera_id=detection_result.camera_id,
                timestamp=detection_result.timestamp,
                total_persons=detection_result.total_count,
                density_score=density_score,
                density_level=density_level,
                regions=regions,
                hotspots=hotspots
            )
            
            # 保存历史记录
            camera_id = detection_result.camera_id
            if camera_id not in self.density_history:
                self.density_history[camera_id] = []
            
            self.density_history[camera_id].append(result)
            
            # 限制历史记录大小
            if len(self.density_history[camera_id]) > 1000:
                self.density_history[camera_id] = self.density_history[camera_id][-1000:]
            
            return result
            
        except Exception as e:
            logger.error(f"Density analysis failed: {e}")
            raise
    
    def _calculate_grid_density(
        self, 
        positions: List[List[float]], 
        width: int, 
        height: int
    ) -> np.ndarray:
        """计算网格密度"""
        # 创建密度网格
        density_grid = np.zeros((self.grid_height, self.grid_width), dtype=np.float32)
        
        if not positions:
            return density_grid
        
        # 计算网格大小
        cell_width = width / self.grid_width
        cell_height = height / self.grid_height
        
        # 统计每个网格中的人数
        for pos in positions:
            x, y = pos
            grid_x = int(x / cell_width)
            grid_y = int(y / cell_height)
            
            # 确保索引在范围内
            grid_x = max(0, min(grid_x, self.grid_width - 1))
            grid_y = max(0, min(grid_y, self.grid_height - 1))
            
            density_grid[grid_y, grid_x] += 1
        
        # 归一化密度
        max_density = np.max(density_grid)
        if max_density > 0:
            density_grid = density_grid / max_density
        
        # 应用平滑滤波
        if self.config.smoothing_factor > 0:
            density_grid = cv2.filter2D(
                density_grid, -1, 
                self.smoothing_kernel * self.config.smoothing_factor
            )
        
        return density_grid
    
    def _get_density_level(self, density_score: float) -> str:
        """获取密度等级"""
        if density_score >= 0.8:
            return "very_high"
        elif density_score >= 0.6:
            return "high"
        elif density_score >= 0.4:
            return "medium"
        elif density_score >= 0.2:
            return "low"
        else:
            return "very_low"
    
    def _analyze_regions(
        self, 
        density_grid: np.ndarray, 
        width: int, 
        height: int
    ) -> List[Dict[str, Any]]:
        """分析区域密度"""
        regions = []
        
        # 计算网格大小
        cell_width = width / self.grid_width
        cell_height = height / self.grid_height
        cell_area = cell_width * cell_height
        
        # 分析每个网格
        for i in range(self.grid_height):
            for j in range(self.grid_width):
                density = density_grid[i, j]
                
                if density > self.config.density_threshold:
                    region = {
                        "region_id": f"region_{i}_{j}",
                        "grid_x": j,
                        "grid_y": i,
                        "center_x": (j + 0.5) * cell_width,
                        "center_y": (i + 0.5) * cell_height,
                        "density": float(density),
                        "area": cell_area,
                        "person_count": int(density * 10)  # 估算人数
                    }
                    regions.append(region)
        
        return regions
    
    def _detect_hotspots(self, positions: List[List[float]]) -> List[Dict[str, Any]]:
        """检测热点区域"""
        if len(positions) < 3:
            return []
        
        try:
            # 使用DBSCAN聚类检测热点
            positions_array = np.array(positions)
            clustering = DBSCAN(
                eps=self.config.hotspot_radius, 
                min_samples=3
            ).fit(positions_array)
            
            hotspots = []
            labels = clustering.labels_
            
            # 分析每个聚类
            for label in set(labels):
                if label == -1:  # 噪声点
                    continue
                
                cluster_points = positions_array[labels == label]
                center = np.mean(cluster_points, axis=0)
                person_count = len(cluster_points)
                
                # 计算聚类半径
                distances = np.linalg.norm(cluster_points - center, axis=1)
                radius = np.max(distances)
                
                hotspot = {
                    "hotspot_id": f"hotspot_{label}",
                    "center": center.tolist(),
                    "radius": float(radius),
                    "person_count": person_count,
                    "density": person_count / (np.pi * radius * radius) if radius > 0 else 0
                }
                
                hotspots.append(hotspot)
            
            # 按人数排序
            hotspots.sort(key=lambda x: x["person_count"], reverse=True)
            
            return hotspots
            
        except Exception as e:
            logger.error(f"Hotspot detection failed: {e}")
            return []
    
    async def generate_heatmap(
        self, 
        detection_result: PersonDetectionResult,
        image_width: int,
        image_height: int
    ) -> HeatmapData:
        """生成热力图"""
        try:
            heatmap_id = str(uuid4())
            
            # 提取人员位置
            person_positions = []
            for person_data in detection_result.persons:
                center = person_data.get("center", [0, 0])
                person_positions.append(center)
            
            # 计算密度网格
            density_grid = self._calculate_grid_density(
                person_positions, image_width, image_height
            )
            
            # 创建热力图数据
            heatmap_data = HeatmapData(
                heatmap_id=heatmap_id,
                camera_id=detection_result.camera_id,
                timestamp=detection_result.timestamp,
                width=image_width,
                height=image_height,
                grid_size=self.config.grid_size,
                data=density_grid.tolist(),
                max_value=float(np.max(density_grid)),
                min_value=float(np.min(density_grid))
            )
            
            # 保存历史记录
            camera_id = detection_result.camera_id
            if camera_id not in self.heatmap_history:
                self.heatmap_history[camera_id] = []
            
            self.heatmap_history[camera_id].append(heatmap_data)
            
            # 限制历史记录大小
            if len(self.heatmap_history[camera_id]) > 100:
                self.heatmap_history[camera_id] = self.heatmap_history[camera_id][-100:]
            
            return heatmap_data
            
        except Exception as e:
            logger.error(f"Heatmap generation failed: {e}")
            raise
    
    async def check_crowd_alerts(
        self, 
        analysis_result: DensityAnalysisResult
    ) -> List[CrowdAlert]:
        """检查人群告警"""
        alerts = []
        
        try:
            # 检查总体密度告警
            if analysis_result.density_score >= self.config.alert_threshold:
                alert = CrowdAlert(
                    alert_id=str(uuid4()),
                    camera_id=analysis_result.camera_id,
                    alert_type="high_density",
                    alert_level=self._get_alert_level(analysis_result.density_score),
                    timestamp=analysis_result.timestamp,
                    person_count=analysis_result.total_persons,
                    density_score=analysis_result.density_score,
                    description=f"检测到高密度人群聚集，密度评分: {analysis_result.density_score:.2f}"
                )
                alerts.append(alert)
            
            # 检查热点告警
            for hotspot in analysis_result.hotspots:
                if hotspot["person_count"] >= 10:  # 热点人数阈值
                    alert = CrowdAlert(
                        alert_id=str(uuid4()),
                        camera_id=analysis_result.camera_id,
                        alert_type="hotspot_gathering",
                        alert_level=AlertLevel.MEDIUM,
                        timestamp=analysis_result.timestamp,
                        person_count=hotspot["person_count"],
                        density_score=hotspot["density"],
                        location={
                            "center": hotspot["center"],
                            "radius": hotspot["radius"]
                        },
                        description=f"检测到热点区域聚集，人数: {hotspot['person_count']}"
                    )
                    alerts.append(alert)
            
            # 保存告警历史
            camera_id = analysis_result.camera_id
            if camera_id not in self.alert_history:
                self.alert_history[camera_id] = []
            
            self.alert_history[camera_id].extend(alerts)
            
            # 限制历史记录大小
            if len(self.alert_history[camera_id]) > 1000:
                self.alert_history[camera_id] = self.alert_history[camera_id][-1000:]
            
            return alerts
            
        except Exception as e:
            logger.error(f"Alert checking failed: {e}")
            return []
    
    def _get_alert_level(self, density_score: float) -> AlertLevel:
        """获取告警级别"""
        if density_score >= 0.9:
            return AlertLevel.CRITICAL
        elif density_score >= 0.8:
            return AlertLevel.HIGH
        elif density_score >= 0.6:
            return AlertLevel.MEDIUM
        else:
            return AlertLevel.LOW
    
    async def get_density_trend(
        self, 
        camera_id: str, 
        hours: int = 1
    ) -> List[DensityAnalysisResult]:
        """获取密度趋势"""
        if camera_id not in self.density_history:
            return []
        
        cutoff_time = datetime.now() - timedelta(hours=hours)
        
        return [
            result for result in self.density_history[camera_id]
            if result.timestamp >= cutoff_time
        ]
    
    async def get_heatmap_history(
        self, 
        camera_id: str, 
        hours: int = 1
    ) -> List[HeatmapData]:
        """获取热力图历史"""
        if camera_id not in self.heatmap_history:
            return []
        
        cutoff_time = datetime.now() - timedelta(hours=hours)
        
        return [
            heatmap for heatmap in self.heatmap_history[camera_id]
            if heatmap.timestamp >= cutoff_time
        ]
    
    async def get_alert_history(
        self, 
        camera_id: str, 
        hours: int = 24
    ) -> List[CrowdAlert]:
        """获取告警历史"""
        if camera_id not in self.alert_history:
            return []
        
        cutoff_time = datetime.now() - timedelta(hours=hours)
        
        return [
            alert for alert in self.alert_history[camera_id]
            if alert.timestamp >= cutoff_time
        ]
    
    def update_config(self, config: DensityConfig):
        """更新配置"""
        self.config = config
        self.grid_width, self.grid_height = config.grid_size
        self.smoothing_kernel = self._create_smoothing_kernel()
        logger.info("Updated density analysis configuration")


class CrowdDensityService:
    """人群密度分析服务"""
    
    def __init__(self):
        self.default_config = DensityConfig()
        self.analyzer = CrowdDensityAnalyzer(self.default_config)
        self.active_analyses: Dict[str, bool] = {}
    
    async def analyze_camera_density(
        self, 
        camera_id: str,
        image_width: int = 1920,
        image_height: int = 1080,
        config: Optional[DensityConfig] = None
    ) -> Optional[DensityAnalysisResult]:
        """分析摄像头的人群密度"""
        try:
            # 获取最新的人员检测结果
            # 这里应该从人员检测服务获取实时检测结果
            # 暂时使用模拟数据
            
            # 创建模拟检测结果
            detection_result = PersonDetectionResult(
                detection_id=str(uuid4()),
                camera_id=camera_id,
                timestamp=datetime.now(),
                persons=self._generate_mock_persons(),
                total_count=0,
                confidence_threshold=0.5
            )
            detection_result.total_count = len(detection_result.persons)
            
            # 使用指定配置或默认配置
            if config:
                analyzer = CrowdDensityAnalyzer(config)
            else:
                analyzer = self.analyzer
            
            # 分析密度
            analysis_result = await analyzer.analyze_density(
                detection_result, image_width, image_height
            )
            
            # 检查告警
            alerts = await analyzer.check_crowd_alerts(analysis_result)
            
            # 处理告警
            for alert in alerts:
                await self._handle_alert(alert)
            
            return analysis_result
            
        except Exception as e:
            logger.error(f"Camera density analysis failed for {camera_id}: {e}")
            return None
    
    def _generate_mock_persons(self) -> List[Dict[str, Any]]:
        """生成模拟人员数据"""
        import random
        
        persons = []
        num_persons = random.randint(0, 20)
        
        for i in range(num_persons):
            x = random.randint(50, 1870)
            y = random.randint(50, 1030)
            w = random.randint(40, 120)
            h = random.randint(120, 250)
            
            person = {
                "person_id": f"mock_person_{i}",
                "bbox": [x, y, x + w, y + h],
                "confidence": random.uniform(0.6, 0.95),
                "center": [x + w/2, y + h/2],
                "features": {
                    "width": w,
                    "height": h,
                    "area": w * h
                }
            }
            persons.append(person)
        
        return persons
    
    async def generate_camera_heatmap(
        self, 
        camera_id: str,
        image_width: int = 1920,
        image_height: int = 1080
    ) -> Optional[HeatmapData]:
        """生成摄像头热力图"""
        try:
            # 创建模拟检测结果
            detection_result = PersonDetectionResult(
                detection_id=str(uuid4()),
                camera_id=camera_id,
                timestamp=datetime.now(),
                persons=self._generate_mock_persons(),
                total_count=0,
                confidence_threshold=0.5
            )
            detection_result.total_count = len(detection_result.persons)
            
            # 生成热力图
            heatmap_data = await self.analyzer.generate_heatmap(
                detection_result, image_width, image_height
            )
            
            return heatmap_data
            
        except Exception as e:
            logger.error(f"Heatmap generation failed for {camera_id}: {e}")
            return None
    
    async def _handle_alert(self, alert: CrowdAlert):
        """处理告警"""
        try:
            # 记录告警日志
            logger.warning(f"CROWD ALERT: {alert.description} - Camera: {alert.camera_id}")
            
            # 这里可以添加告警通知逻辑
            # 例如：发送邮件、短信、推送通知等
            
            # 可以集成到告警管理系统
            # await alert_manager.send_alert(alert)
            
        except Exception as e:
            logger.error(f"Alert handling failed: {e}")
    
    async def start_continuous_analysis(self, camera_id: str):
        """启动连续分析"""
        if camera_id in self.active_analyses:
            return
        
        self.active_analyses[camera_id] = True
        
        # 启动后台任务进行连续分析
        asyncio.create_task(self._continuous_analysis_loop(camera_id))
        
        logger.info(f"Started continuous density analysis for camera {camera_id}")
    
    async def stop_continuous_analysis(self, camera_id: str):
        """停止连续分析"""
        if camera_id in self.active_analyses:
            self.active_analyses[camera_id] = False
            logger.info(f"Stopped continuous density analysis for camera {camera_id}")
    
    async def _continuous_analysis_loop(self, camera_id: str):
        """连续分析循环"""
        while self.active_analyses.get(camera_id, False):
            try:
                await self.analyze_camera_density(camera_id)
                await asyncio.sleep(30)  # 每30秒分析一次
            except Exception as e:
                logger.error(f"Continuous analysis error for {camera_id}: {e}")
                await asyncio.sleep(60)  # 出错时等待更长时间
    
    async def get_density_statistics(self, camera_id: str) -> Dict[str, Any]:
        """获取密度统计信息"""
        try:
            # 获取最近24小时的密度趋势
            trend_data = await self.analyzer.get_density_trend(camera_id, hours=24)
            
            if not trend_data:
                return {
                    "camera_id": camera_id,
                    "total_analyses": 0,
                    "average_density": 0,
                    "max_density": 0,
                    "alert_count": 0
                }
            
            # 计算统计信息
            densities = [result.density_score for result in trend_data]
            person_counts = [result.total_persons for result in trend_data]
            
            # 获取告警数量
            alerts = await self.analyzer.get_alert_history(camera_id, hours=24)
            
            return {
                "camera_id": camera_id,
                "total_analyses": len(trend_data),
                "average_density": sum(densities) / len(densities),
                "max_density": max(densities),
                "min_density": min(densities),
                "average_person_count": sum(person_counts) / len(person_counts),
                "max_person_count": max(person_counts),
                "alert_count": len(alerts),
                "last_analysis": trend_data[-1].timestamp.isoformat()
            }
            
        except Exception as e:
            logger.error(f"Statistics calculation failed for {camera_id}: {e}")
            return {}
    
    def update_config(self, config: DensityConfig):
        """更新配置"""
        self.default_config = config
        self.analyzer.update_config(config)
    
    def get_config(self) -> DensityConfig:
        """获取配置"""
        return self.default_config


# 全局人群密度分析服务实例
crowd_density_service = CrowdDensityService()